-- Copyright (c) 2015,2016 Lucky Byte, Inc.
--
-- Summary : 手续费模块
--
-- Author  : 吴小虎 <xiaohu@lucky-byte.com>
-- Since   : 2016.4.29
--
-- History
--  * 2015.4.29 : 创建此文件
--  * 2015.7.09 : 借贷记分别计算
--  * 2015.7.27 : 获取保底金额
--

-- 按金额上限排序，从小到大
local _sort_by_max_amt = function(v1, v2)
    if v1.amt_max <= 0.0 then
        return false
    end
    if v2.amt_max <= 0.0 then
        return true
    end
    return v2.amt_max > v1.amt_max
end


-- 检查手续费是否有效，无效则给出警告
local _check_fee = function(fee, tramt)
    if fee < 0.0 then
        notify.warn('计算出手续费[%s]小于[0]，请检查配置', fee)
    end
    if fee >= tramt then
        notify.warn('计算出手续费[%s]大于或等于交易金额[%s]，请检查配置', fee, tramt)
    end
    return fee
end


-- 通过指定的规则计算手续费
local _calc_by_rule = function(tramt, rule)
    -- 固定额度
    if rule.fix then
        logger.info('金额[%s]匹配到手续费上限[%s], 根据规则计算手续费为定额[%s]元.',
            tramt, rule.amt_max, rule.fee)
        return _check_fee(rule.fee, tramt)
    end

    -- 按比例收取
    if rule.fee > 100 then
        logger.error('手续费中定义上限为[%s]的规则有误，收取手续费[%s%%]比例超出 100%',
            rule.amt_max, rule.fee)
        return -1
    end
    local _fee = round(tramt * rule.fee / 100)

    logger.info('金额[%s]匹配到手续费上限[%s]，根据规则计算手续费为[%s x %s%% = %s]元.',
        tramt, rule.amt_max, tramt, rule.fee, _fee)

    -- 按比例收取需考虑保底和封顶金额
    if rule.min and rule.min > 0.0 then
        if _fee < rule.min then
            logger.info('手续费[%s]低于保底金额[%s]，使用保底金额.', _fee, rule.min)
            _fee = rule.min
        end
    end
    if rule.max and rule.max > 0.0 then
        if _fee > rule.max then
            logger.info('手续费[%s]高于封顶金额[%s]，使用封顶金额.', _fee, rule.max)
            _fee = rule.max
        end
    end
    return _check_fee(_fee, tramt)
end


-- 检查手续费规则
--
local _check_rules = function(uuid, is_debit)
    -- 查询手续费配置
    local _table = jdbc.select([[ select * from pay_fee where uuid = ? ]], uuid)
    assert(_table and _table.count > 0,
        string.format('未查询到手续费配置[%s]，请检查.', uuid))
    local _record = _table.get_record(0)

    -- 通过借贷记选择不同的规则
    local _rules = _record.get(is_debit and 'debit_rules' or 'cedit_rules')
    if not _rules then
        logger.error('手续费配置[%s]规则定义不完整.', _record.get('name'))
        return nil
    end
    local _rules = core.json.decode(_rules)
    if not _rules then
        logger.error('手续费配置[%s]中定义的规则无效.', uuid)
        return nil
    end

    -- 按金额上限排序
    table.sort(_rules, _sort_by_max_amt)

    -- 检查金额上限重复
    local _last_amt_max = -1
    for _, _rule in ipairs(_rules) do
        if _rule.amt_max == _last_amt_max then
            notify.error('手续费[%s]中重复定义了金额上限[%s]，请改正.',
                _record.get('name'), _last_amt_max)
            return nil
        end
        _last_amt_max = _rule.amt_max
    end
    return _rules
end


-- 计算手续费
--
-- args.uuid:   手续费定义 UUID
-- args.tramt:  金额(number)，以元为单位
-- args.debit:  借贷记标志
--
-- Return:  手续费(number) or -1
--
local _calc = function(args)
    if not args or not args.uuid or not args.tramt then
        logger.fatal('fee.calc() 参数缺少必须的字段.')
        return nil
    end

    -- 通过卡号判定借贷记，然后获取相应的手续费规则
    local _rules = _check_rules(args.uuid, args.debit)
    if not _rules then
        logger.error('检查手续费规则错误，计算手续费失败.')
        return -1
    end

    -- 按照规则中的金额上限逐个匹配
    for _, _rule in ipairs(_rules) do
        if _rule.amt_max <= 0.0 then
            return _calc_by_rule(args.tramt, _rule)
        end
        if args.tramt < _rule.amt_max then
            return _calc_by_rule(args.tramt, _rule)
        end
    end
    notify.error('金额[%s]不能匹配到手续费[%s]中定义的任何规则，计算手续费失败.',
        args.tramt, args.uuid)
    return -1;
end


-- 通过借贷记标志取保底金额
local _lowest_debit = function(uuid, tramt, is_debit)
    local _rules = _check_rules(uuid, is_debit)
    if not _rules then
        logger.error('检查手续费规则错误，计算手续费失败.')
        return -1
    end
    -- 按照规则中的金额上限逐个匹配
    for _, _rule in ipairs(_rules) do
        if _rule.amt_max <= 0.0 then
            return _rule.min
        end
        if tramt < _rule.amt_max then
            return _rule.min
        end
    end
    notify.error('金额[%s]不能匹配到手续费[%s]中定义的任何规则，计算手续费失败.',
        tramt, uuid)
    return -1;
end


-- 取保底金额
--
-- args.uuid:    手续费定义 UUID
-- args.tramt:   金额(number)，以元为单位
--
local _lowest = function(args)
    if not args or not args.uuid or not args.tramt then
        logger.fatal('fee.lowest() 参数缺少必须的字段.')
        return -1
    end
    return _lowest_debit(args.uuid, args.tramt, true),
           _lowest_debit(args.uuid, args.tramt, false)
end


-- 取借贷记最大的保底金额
--
local _lowest_max = function(args)
    local _lowest1, _lowest2 = _lowest(args)
    if _lowest1 < 0.0 or _lowest2 < 0.0 then
        return -1
    end
    return _lowest1 > _lowest2 and _lowest1 or _lowest2
end


-- 外部接口
core = core or {}
core.fee = {
    calc = _calc,
    lowest = _lowest,
    lowest_max = _lowest_max
}
